import os.path
import sys
import re

from PySide6.QtWidgets import QWidget, QVBoxLayout, QHBoxLayout, QLabel, QPushButton, QMessageBox, \
    QTableWidget, QTableWidgetItem, QApplication, QAbstractItemView, QProgressDialog, QFileDialog, QLineEdit
from PySide6.QtGui import Qt
from easy_tools.utils.file import get_user_read_size, get_dir_or_file_size
from PySide6.QtCore import QThread, Signal, QObject


class WorkerThread(QObject):
    scan = Signal(dict)
    dialog = Signal(dict)

    def __init__(self, widget):
        super().__init__()
        self.widget = widget

    def scan_proc(self):
        widget = self.widget
        if not widget.input_path_widget.text():
            widget.scan.emit({"success": 0, 'title': '错误', 'msg': '扫描地址不能为空'})
            return
        dir_or_file_path = widget.input_path_widget.text()
        if not os.path.exists(dir_or_file_path):
            widget.scan.emit({'success': 0, 'title': '错误', 'msg': '该目录/文件地址不存在, 请检查后重新尝试'})
            return
        exclude_paths = []
        if widget.input_exclude_path.text():
            exclude_paths = [one.replace('\\', '/') for one in widget.input_exclude_path.text().split(',') if one]
        if dir_or_file_path in exclude_paths:
            return

        dir_or_file_path = dir_or_file_path.replace('\\', '/')
        if os.path.isfile(dir_or_file_path):
            file_name = dir_or_file_path[-dir_or_file_path[::-1].index('/'):]
            self.scan.emit({'success': 1, 'data': [[file_name, get_user_read_size(os.path.getsize(dir_or_file_path), 2), dir_or_file_path]]})
        elif os.path.isdir(dir_or_file_path):
            self._scan_dir_proc(dir_or_file_path, exclude_paths)

    def _scan_dir_proc(self, dir_path, exclude_path=None):
        if exclude_path is None:
            exclude_path = []
        if dir_path in exclude_path:
            return
        dir_and_files = os.listdir(dir_path)
        self.dialog.emit({'status': 1, 'data': {'total': len(dir_and_files)}})

        infos = []
        for i in range(len(dir_and_files)):
            sub_ = dir_and_files[i]
            sub_path = os.path.join(dir_path, sub_).replace('\\', '/')
            if sub_path not in exclude_path and sub_ not in exclude_path:
                size = get_dir_or_file_size(sub_path)
                if os.path.isdir(sub_path):
                    sub_ += '/'
                infos.append([sub_, size, sub_path])
                self.dialog.emit({'status': 2, 'data': {'value': i + 1}})
            else:
                infos.append([sub_, -1, sub_path])
                self.dialog.emit({'status': 2, 'data': {'value': i + 1}})

        infos.sort(key=lambda x: x[1], reverse=True)

        for i in range(len(dir_and_files)):
            for j in range(len(infos[i])):
                if j == 1:
                    infos[i][j] = get_user_read_size(infos[i][j], 2)

        self.dialog.emit({'status': 0})
        self.scan.emit({'success': 1, 'data': infos})


class SystemStorageScanWidget(QWidget):

    def __init__(self):
        super().__init__()
        self.table = None
        self.open_file_map = {}
        self._init()

    def _init(self):
        self.thread = QThread(self)
        self.worker = WorkerThread(self)
        self.worker.moveToThread(self.thread)

        self.vbox = QVBoxLayout(self)
        self.vbox.setAlignment(Qt.AlignmentFlag.AlignHCenter | Qt.AlignmentFlag.AlignTop)
        self.setLayout(self.vbox)
        self.input_path_widget = QLabel('', self)
        self.input_choice_widget = QPushButton('选择目录', self)
        self.input_exclude_path = QLineEdit('', self)
        self.confirm = QPushButton('扫描', self)

        input_layout = QHBoxLayout(self)
        input_layout.addWidget(QLabel('扫描的目录: ', self), 1)
        input_layout.addWidget(self.input_path_widget, 2)
        input_layout.addWidget(self.input_choice_widget, 1)

        input_layout2 = QHBoxLayout(self)
        input_layout2.addWidget(QLabel('过滤目录/文件: ', self), 1)
        input_layout2.addWidget(self.input_exclude_path, 3)

        self.vbox.addLayout(input_layout)
        self.vbox.addLayout(input_layout2)
        self.vbox.addWidget(self.confirm)

        self.worker.dialog.connect(self._scan_dialog_callable)
        self.worker.scan.connect(self._scan_callback)
        self.input_choice_widget.clicked.connect(self._choice_dir)
        self.confirm.clicked.connect(self.worker.scan_proc)
        self.thread.start()

    def _choice_dir(self):
        dir_name = QFileDialog.getExistingDirectory(self)
        if dir_name:
            self.input_path_widget.setText(dir_name)
            self.input_choice_widget.setText('重新选择')

    def _scan_dialog_callable(self, res):
        if res['status'] == 1:
            self.dialog = QProgressDialog(self)
            self.dialog.setWindowTitle('提示')
            self.dialog.setLabelText('正在扫描中...')
            self.dialog.setRange(0, res['data']['total'])
            self.dialog.setValue(0)
            self.dialog.setCancelButtonText(None)
            self.dialog.setWindowFlags(Qt.WindowType.Dialog | Qt.WindowType.CustomizeWindowHint | Qt.WindowType.WindowMinimizeButtonHint)
            self.dialog.show()
        elif res['status'] == 0:
            self.dialog.close()
        elif res['status'] == 2:
            self.dialog.setValue(res['data']['value'])

    def _scan_callback(self, ans):
        if not ans['success']:
            QMessageBox.question(self, ans['title'], ans['msg'], QMessageBox.StandardButton.Ok)
            return

        if self.table:
            self.vbox.removeWidget(self.table)
            self.table.close()
            self.open_file_map.clear()
            self.open_file_map = {}
        self.table = self._get_table(len(ans['data']), 4)
        for i in range(len(ans['data'])):
            info = ans['data'][i]
            for j in range(len(info)):
                self.table.setItem(i, j, QTableWidgetItem(info[j]))
            cur_path = info[len(info) - 1]
            option = QWidget()
            hbox = QHBoxLayout(option)
            hbox.setContentsMargins(5, 2, 5, 2)
            option.setLayout(hbox)
            if os.path.isdir(cur_path):
                b = QPushButton('扫描该位置', option)
                b.clicked.connect(self._scan_another_path)
                self.open_file_map[b] = cur_path
                hbox.addWidget(b)

            b = QPushButton('打开所在目录' if os.path.isfile(cur_path) else '打开该目录', option)
            b.clicked.connect(self._open_os_dir)
            self.open_file_map[b] = cur_path
            hbox.addWidget(b)
            self.table.setCellWidget(i, len(info), option)

        if self.table:
            self.vbox.addWidget(self.table)

        QMessageBox.question(self, '提示', '扫描完成', QMessageBox.StandardButton.Ok)

    def _scan_another_path(self):
        dir_path = self.open_file_map[self.sender()]
        self.input_path_widget.setText(dir_path)
        self.input_exclude_path.setText('')
        self.worker.scan_proc()

    def _open_os_dir(self):
        dir_path = self.open_file_map[self.sender()]
        if os.path.isfile(dir_path):
            dir_path = dir_path[:-dir_path[::-1].index('/')]
        os.startfile(dir_path)

    def _get_table(self, line, column):
        table = QTableWidget(line, column, self)
        table.setEditTriggers(QAbstractItemView.EditTrigger.NoEditTriggers)
        table.setHorizontalHeaderLabels(['文件名', '大小', '文件路径', '操作'])
        return table

    def close(self) -> bool:
        if self.table:
            self.table.close()
        if self.thread:
            self.thread.quit()
        return super().close()


if __name__ == '__main__':
    app = QApplication()
    win = SystemStorageScanWidget()
    win.show()
    sys.exit(app.exec())
